﻿using System;
using AForge;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using MatrixDataStructures;
using System.Drawing;
using CommonNamespace;

namespace SPARFilter
{
    class Program
    {
        static void Main(string[] args)
        {
            {
                
                MatrixDataStructures.BasicPixelSizeMatrixInterpolation resizer = new MatrixDataStructures.BasicPixelSizeMatrixInterpolation();
                string sParPath="", hdrPath="", parPath = "";
                bool forceRotate = false;

                # region "Manage Command Line options"
                switch (args.Length)
                {
                    case 0:
                        {
                            System.Diagnostics.Process p = new System.Diagnostics.Process();
                            p.StartInfo.FileName = "cmd.exe";
                            p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Maximized;
                            p.StartInfo.WorkingDirectory = Application.StartupPath;
                            p.StartInfo.Arguments = "/k sparfilter.exe /?";
                            p.Start();
                    
                            return;
                        }
                    case 1:
                        {
                            Console.Write("Usage: SParFilter.exe sParPath hdrPath [parPath] [forceRotate] [rotationBuffer]");
                            return;
                            
                        }
                    case 2:
                        {
                            sParPath = args[0].ToString();
                            hdrPath = args[1].ToString();
                            break; }
                    case 3:
                        {
                            sParPath = args[0].ToString();
                            hdrPath = args[1].ToString();
                            parPath = args[2].ToString();
                            break;
                        }
                    case 4:
                        {
                            sParPath = args[0].ToString();
                            hdrPath = args[1].ToString();
                            parPath = args[2].ToString();
                            forceRotate = (args[3].ToString().ToLower().Trim() == "y" ? true : false);
                            break;
                        }
                    case 5:
                        {
                            sParPath = args[0].ToString();
                            hdrPath = args[1].ToString();
                            parPath = args[2].ToString();
                            forceRotate = (args[3].ToString().ToLower().Trim() == "y" ? true : false);
                            break;
                        }
                }
                # endregion

                # region "Load Files, Print header info, use Par if needed"
                Console.WriteLine();
                SingleVolume filterVolume = SPARParser.Parser.Parse(sParPath);
                Console.WriteLine();

                string outputFileName = System.IO.Path.GetDirectoryName(sParPath) + System.IO.Path.DirectorySeparatorChar + System.IO.Path.GetFileNameWithoutExtension(hdrPath) + "_" + System.IO.Path.GetFileNameWithoutExtension(sParPath).ToString();

                Console.WriteLine("Output Directories : " + outputFileName);

                SingleVolume AnatomyVolume = HdrImgReader.ReadFromHdrFile(hdrPath, HdrImgReader.OutPutType.auto);

                Console.WriteLine("Data Center: " + AnatomyVolume.Center.ToString());
                Console.WriteLine("Data FOV: " + AnatomyVolume.FOV.ToString());
                Console.WriteLine("Data Angulation: " + AnatomyVolume.OffCenterAngle.ToString());

                Console.WriteLine();
                Console.WriteLine("VOI Center: " + filterVolume.Center.ToString());
                Console.WriteLine("VOI FOV: " + filterVolume.FOV.ToString());
                Console.WriteLine("VOI Angulation: " + filterVolume.OffCenterAngle.ToString());

                Console.WriteLine();
                if (args.Length == 0 || args.Length >= 3)
                {
                    Console.Write("Read Header Values from PAR file, which might take long (y/n): ");
                    ConsoleKeyInfo inputKey = Console.ReadKey(false);
                    if (inputKey.KeyChar == 'y')
                    {
                        Console.WriteLine();
                        MetaView.SingleOptimizedParRecLoader parLoader = new MetaView.SingleOptimizedParRecLoader(parPath);
                        SingleVolume tempVolume = parLoader.GetData();
                        AnatomyVolume.OffCenterAngle = tempVolume.OffCenterAngle;
                        AnatomyVolume.Center = tempVolume.Center;
                        AnatomyVolume.FOV = tempVolume.FOV;
                        Console.WriteLine("Data Center: " + AnatomyVolume.Center.ToString());
                        Console.WriteLine("Data FOV: " + AnatomyVolume.FOV.ToString());
                        Console.WriteLine("Data Angulation: " + AnatomyVolume.OffCenterAngle.ToString());
                    }
                }
                # endregion

                Console.WriteLine();
                Console.WriteLine("Starting Process ... ");

                # region "Fill Filter with numbers, then isovoxel, then padd with 0s before rotating. Also isovoxel anatomy"
                for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                    for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                        for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                            filterVolume.Matrix[i, j, k] = 255;
                
                bool isDecimal = (AnatomyVolume.MatrixD != null);
                if (isDecimal)
                    AnatomyVolume.Matrix = new Matrix3DInt16(AnatomyVolume.MatrixD.x, AnatomyVolume.MatrixD.y, AnatomyVolume.MatrixD.z);

                if (filterVolume.FOV.x != filterVolume.Matrix.x | filterVolume.FOV.y != filterVolume.Matrix.y | filterVolume.FOV.z != filterVolume.Matrix.z)
                {
                    MatrixResizer.MatrixResize isovoxeler = new MatrixResizer.MatrixResize(8); //8 is the choice of method to choose for resizing
                    filterVolume.Matrix = isovoxeler.Resize(filterVolume.Matrix, filterVolume.FOV.x, filterVolume.FOV.y, filterVolume.FOV.z);
                }
                if (AnatomyVolume.FOV.x != AnatomyVolume.Matrix.x | AnatomyVolume.FOV.y != AnatomyVolume.Matrix.y | AnatomyVolume.FOV.z != AnatomyVolume.Matrix.z)
                {
                    MatrixResizer.MatrixResize isovoxeler = new MatrixResizer.MatrixResize(8);
                    filterVolume.Matrix = isovoxeler.Resize(filterVolume.Matrix, filterVolume.FOV.x, filterVolume.FOV.y, filterVolume.FOV.z);
                }

                Matrix3DSingle resizedAnatomyVolume ;
                
                if (isDecimal)
                    AnatomyVolume.Matrix = AnatomyVolume.MatrixD.ReScaleToINT(AnatomyVolume.MatrixD, 0f, 10000f);

                resizedAnatomyVolume= resizer.ResizePixelsReturnSingle(AnatomyVolume.Matrix, AnatomyVolume.FOV.x, AnatomyVolume.FOV.y, AnatomyVolume.FOV.z);
                
                Matrix3DInt16 newfilter = new Matrix3DInt16(resizedAnatomyVolume.x, resizedAnatomyVolume.y, resizedAnatomyVolume.z);
                if (!resizedAnatomyVolume.Size().IsAbsolutelyBiggerThanOrEqualTo(filterVolume.Matrix.Size()))
                {
                    Console.WriteLine("Filter is bigger than anatomy. Cannot continue.");
                    return; // TODO: might not be correct. Was : Exit Sub
                }
                
                //Rotate before alligning to center since VOI is usually rotated about it's own center during design time.
                //inflate first so that rotation is within bounds
                const int inflationQuotient = 4;
                int newx = filterVolume.Matrix.x * inflationQuotient;
                int newy = filterVolume.Matrix.y * inflationQuotient;
                int newz = filterVolume.Matrix.z * inflationQuotient;
                Matrix3DInt16 newfiltervolume = new Matrix3DInt16(newx, newy, newz);
                CoordinateD matrixc;
                for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                    for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                        for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                            newfiltervolume[(int)(newfiltervolume.x - filterVolume.Matrix.x) / 2 + i, (int)(newfiltervolume.y - filterVolume.Matrix.y) / 2 + j, (int)(newfiltervolume.z - filterVolume.Matrix.z) / 2 + k] = filterVolume.Matrix[i, j, k];
                
                filterVolume.Matrix = newfiltervolume;
                HdrImgWriter.WriteToFile(new SingleVolume(filterVolume.Matrix, null, filterVolume.FOV, filterVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_inflatedvoi");
                # endregion

                # region "Fixing Angulation from VOI "
                // changed 08/12
                Console.WriteLine("Fixing angulation based on VOI ...");
                AForge.Imaging.Filters.Rotate filter = new AForge.Imaging.Filters.Rotate(-1*Convert.ToSingle(filterVolume.OffCenterAngle.z), AForge.Imaging.InterpolationMethod.Bicubic,true);
                if ((filterVolume.OffCenterAngle.z != 0)||(forceRotate))
                {
                    Console.WriteLine("Fixing z-angulation: " + filterVolume.OffCenterAngle.z.ToString());                    
                    for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                    {
                        Bitmap b = filter.Apply(filterVolume.Matrix.SliceToBitmap(filterVolume.Matrix.TransverseSlice(k)));
                        for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                            for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                                filterVolume.Matrix[i, j, k] = b.GetPixel(i, j).R;
                    }
                }
                if ((filterVolume.OffCenterAngle.y != 0) || (forceRotate))
                {
                    Console.WriteLine("Fixing y-angulation: " + filterVolume.OffCenterAngle.y.ToString());
                    filter = new AForge.Imaging.Filters.Rotate(Convert.ToSingle(filterVolume.OffCenterAngle.y), AForge.Imaging.InterpolationMethod.Bicubic, true);
                    for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                    {
                        Bitmap b = filter.Apply(filterVolume.Matrix.SliceToBitmap(filterVolume.Matrix.CoronalSlice(j)));
                        for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                            for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                                filterVolume.Matrix[i, j, filterVolume.Matrix.z - 1 - k] = b.GetPixel(i, k).R;
                    }
                }
                if ((filterVolume.OffCenterAngle.x != 0) || (forceRotate))
                {
                    Console.WriteLine("Fixing x-angulation: " + filterVolume.OffCenterAngle.x.ToString());
                    filter = new AForge.Imaging.Filters.Rotate(Convert.ToSingle(filterVolume.OffCenterAngle.x), AForge.Imaging.InterpolationMethod.Bicubic, true);
                    for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                    {
                        Bitmap b = filter.Apply(filterVolume.Matrix.SliceToBitmap(filterVolume.Matrix.SagittalSlice(i)));
                        for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                            for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                                filterVolume.Matrix[i, filterVolume.Matrix.y - 1 - j, filterVolume.Matrix.z - 1 - k] = b.GetPixel(j, k).R;
                    }
                }

                HdrImgWriter.WriteToFile(new SingleVolume(filterVolume.Matrix, null, filterVolume.FOV, filterVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_inflatedvoi_voiangulationcorrected");
                #endregion

                # region "Extracting Coordinates from Rotated-Filter, allign coordinates to Center"
                long count = 0;
                long countskipped = 0;

                for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                {
                    for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                    {
                        for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                        {
                            count += 1;
                            matrixc = new CoordinateD(
                                (resizedAnatomyVolume.x - filterVolume.Matrix.x) / 2 + i - (AnatomyVolume.Center.x + filterVolume.Center.x),
                                (resizedAnatomyVolume.y - filterVolume.Matrix.y) / 2 + j - (AnatomyVolume.Center.y + filterVolume.Center.y),
                                (resizedAnatomyVolume.z - filterVolume.Matrix.z) / 2 + k - (AnatomyVolume.Center.z - filterVolume.Center.z));

                            if (matrixc.x < 0 | matrixc.y < 0 | matrixc.z < 0 | matrixc.x >= newfilter.x | matrixc.y >= newfilter.y | matrixc.z >= newfilter.z)
                            {
                                countskipped += 1;
                                continue;
                            }
                            Int16 a = Convert.ToInt16(Math.Round(matrixc.x));
                            Int16 b = Convert.ToInt16(Math.Round(matrixc.y));
                            Int16 c = Convert.ToInt16(Math.Round(matrixc.z));
                            if (a>0 && a< newfilter.x && b>0 && b<newfilter.y && c>0 && c<newfilter.z)
                                newfilter[a,b,c] = filterVolume.Matrix[i, j, k];
                        }                          
                    }
                }
                
                if (countskipped > 0)
                    Console.WriteLine("Skipped " + countskipped + " out of " + count);
                filterVolume.Matrix = newfilter;
                HdrImgWriter.WriteToFile(new SingleVolume(filterVolume.Matrix, null, AnatomyVolume.FOV, AnatomyVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_inflatedvoi_voiangulationcorrected_reg");
                # endregion

                # region "Fixing Angulation from Volume"
                Console.WriteLine("Fixing angulation based on data ...");
                if ((AnatomyVolume.OffCenterAngle.z != 0))
                {
                    Console.WriteLine("Fixing z-angulation: " + AnatomyVolume.OffCenterAngle.z.ToString());
                    filter = new AForge.Imaging.Filters.Rotate(Convert.ToSingle( AnatomyVolume.OffCenterAngle.z), AForge.Imaging.InterpolationMethod.Bicubic, true);
                    for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                    {
                        Bitmap b = filter.Apply(filterVolume.Matrix.SliceToBitmap(filterVolume.Matrix.TransverseSlice(k)));
                        for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                        {
                            for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                            {
                                filterVolume.Matrix[i, j, k] = b.GetPixel(i, j).R;
                            }
                        }
                    }
                }
                if ((AnatomyVolume.OffCenterAngle.y != 0))
                {
                    Console.WriteLine("Fixing y-angulation: " + AnatomyVolume.OffCenterAngle.y.ToString());
                    filter = new AForge.Imaging.Filters.Rotate(Convert.ToSingle(-1 * AnatomyVolume.OffCenterAngle.y), AForge.Imaging.InterpolationMethod.Bicubic, true);
                    for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                    {
                        Bitmap b = filter.Apply(filterVolume.Matrix.SliceToBitmap(filterVolume.Matrix.CoronalSlice(j)));
                        for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                        {
                            for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                            {
                                filterVolume.Matrix[i, j, filterVolume.Matrix.z - 1 - k] = b.GetPixel(i, k).R;
                            }
                        }
                    }
                }
                if ((AnatomyVolume.OffCenterAngle.x != 0))
                {
                    Console.WriteLine("Fixing x-angulation: " + AnatomyVolume.OffCenterAngle.x.ToString());
                    filter = new AForge.Imaging.Filters.Rotate(Convert.ToSingle(-1 * AnatomyVolume.OffCenterAngle.x), AForge.Imaging.InterpolationMethod.Bicubic, true);
                    for (int i = 0; i <= filterVolume.Matrix.x - 1; i++)
                    {
                        Bitmap b = filter.Apply(filterVolume.Matrix.SliceToBitmap(filterVolume.Matrix.SagittalSlice(i)));
                        for (int j = 0; j <= filterVolume.Matrix.y - 1; j++)
                        {
                            for (int k = 0; k <= filterVolume.Matrix.z - 1; k++)
                            {
                                filterVolume.Matrix[i, filterVolume.Matrix.y - 1 - j, filterVolume.Matrix.z - 1 - k] = b.GetPixel(j, k).R;
                            }
                        }
                    }
                }
                HdrImgWriter.WriteToFile(new SingleVolume(filterVolume.Matrix, null, AnatomyVolume.FOV, AnatomyVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_inflatedvoi_voidataangulationcorrected");
                #endregion
                
                Matrix3DInt16 restoredAnatomyVolume = resizer.ResizePixels(filterVolume.Matrix, AnatomyVolume.Matrix.x, AnatomyVolume.Matrix.y, AnatomyVolume.Matrix.z);
                Matrix3DSingle VOIprobMap = new Matrix3DSingle(restoredAnatomyVolume.x, restoredAnatomyVolume.y, restoredAnatomyVolume.z);
                for (int i = 0; i <= restoredAnatomyVolume.x - 1; i++)
                    for (int j = 0; j <= restoredAnatomyVolume.y - 1; j++)
                        for (int k = 0; k <= restoredAnatomyVolume.z - 1; k++)
                            VOIprobMap[i,j,k] = Convert.ToSingle(Convert.ToSingle(restoredAnatomyVolume[i,j,k])/255f);
                
                HdrImgWriter.WriteToFile(new SingleVolume(null, VOIprobMap, AnatomyVolume.FOV, AnatomyVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_fov_prob");
               
                short max2 = (short)(resizedAnatomyVolume.Maximum() * 2);
                SingleVolume filterdata = new SingleVolume(null, new  Matrix3DSingle(AnatomyVolume.Matrix.x, AnatomyVolume.Matrix.y, AnatomyVolume.Matrix.z), AnatomyVolume.FOV, AnatomyVolume.Center, new CoordinateD(0,0,0));
                count = 0;
                for (int i = 0; i <= restoredAnatomyVolume.x - 1; i++)
                {
                    for (int j = 0; j <= restoredAnatomyVolume.y - 1; j++)
                    {
                        for (int k = 0; k <= restoredAnatomyVolume.z - 1; k++)
                        {
                            if (restoredAnatomyVolume[i, j, k] != 0)
                            {
                                if (isDecimal)
                                    filterdata.MatrixD[i, j, k] = AnatomyVolume.MatrixD[i, j, k];
                                else
                                    filterdata.MatrixD[i, j, k] = AnatomyVolume.Matrix[i, j, k];
                                AnatomyVolume.Matrix[i, j, k] = restoredAnatomyVolume[i,j,k];
                                count += 1;
                            }

                        }
                    }
                }

                # region "Save Results"
                    HdrImgWriter.WriteToFile(new SingleVolume(AnatomyVolume.Matrix,null, AnatomyVolume.FOV, AnatomyVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_fov");
                    HdrImgWriter.WriteToFile(new SingleVolume(null, filterdata.MatrixD, AnatomyVolume.FOV, AnatomyVolume.Center, new CoordinateD(0, 0, 0)), outputFileName + "_data");
                # endregion

                # region "Print #voxels and exit"
                    Console.WriteLine();
                    Console.WriteLine("Valid # of Voxels: " + count);
                    # endregion
            }
        }
    }
}
